// (C) 2022 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Intel Program License Subscription 
// Agreement, Intel FPGA IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Intel and sold by 
// Intel or its authorized distributors.  Please refer to the applicable 
// agreement for further details.

module A_cse_ocs_dpa_aes10c_gf16_sq_inv_masked
   #(
      parameter OCS_AES_DPA_USE_CTECH = 1,
      parameter BETA  = 4'b1011,
      parameter ALPHA = 4'b0001,   
      parameter POLY1 = 3
   )
   (   
      input  logic [7:0] a,
      input  logic [7:0] a_mask,
      input  logic [7:0] o_mask,
      output logic [7:0] o
   );
   
   wire [3:0]         a_h, a_l, a_mask_h, a_mask_l, o_mask_h, o_mask_l;
   wire [3:0]         a_scaled, a_mask_scaled, o_h, o_l;
   wire [3:0]	      prod_ah_alpha, prod_maskh_alpha;
   
   wire [3:0]         sum_hl, sum_hl_mask, p0, p1, x0;
   wire [3:0]         inv_in, inv_in_mask, inv;

   // Continuous assignments
   assign a_h = a[7:4];
   assign a_l = a[3:0];

   assign a_mask_h = a_mask[7:4];
   assign a_mask_l = a_mask[3:0];

   assign o_mask_h = o_mask[7:4];
   assign o_mask_l = o_mask[3:0];
   
   assign o = {o_h, o_l};

   assign sum_hl = prod_ah_alpha ^ a_l;
   assign sum_hl_mask = prod_maskh_alpha ^ a_mask_l;

   // Module instantiations
  
   // a_h^2 * beta 
   A_cse_ocs_dpa_aes10c_gf16_sq_scaler #(.POLY1(POLY1), .BETA(BETA)) scaler0
	(
          .a (a_h),
	  .o (a_scaled)		
	);
   
   // mask_h^2 * beta
   A_cse_ocs_dpa_aes10c_gf16_sq_scaler #(.POLY1(POLY1), .BETA(BETA)) scaler1
	(
	  .a (a_mask_h),
	  .o (a_mask_scaled)
	);

   // a_h * alpha
   A_cse_ocs_dpa_aes10c_gf16_const_mul #(.POLY1(POLY1), .a(ALPHA)) const_mul0
	(
	  .b (a_h),
	  .o (prod_ah_alpha)
	);

   // mask_h * alpha
   A_cse_ocs_dpa_aes10c_gf16_const_mul #(.POLY1(POLY1), .a(ALPHA)) const_mul1
	(
	  .b (a_mask_h),
	  .o (prod_maskh_alpha)
	);

   A_cse_ocs_dpa_aes10c_gf16_mul_masked
        #( .OCS_AES_DPA_USE_CTECH(OCS_AES_DPA_USE_CTECH),
           .POLY1(POLY1)) 
     mul_masked0
	(
	  .a 	  (sum_hl),
	  .a_mask (sum_hl_mask),
	  .b	  (a_l),
	  .b_mask (a_mask_l),
	  .o 	  (p0),
	  .o_mask (o_mask_h)	
	);

   assign p1 = p0 ^ a_scaled;

   assign inv_in = p1;
   assign inv_in_mask = a_mask_scaled ^ o_mask_h;

   // GF(16) inverse computation
   A_cse_ocs_dpa_aes10c_gf16_inv_masked
      #( .OCS_AES_DPA_USE_CTECH(OCS_AES_DPA_USE_CTECH),
         .POLY1(POLY1))
     inv0
	(
	  .a 	     (inv_in),
	  .a_mask    (inv_in_mask),
	  .o_mask    (a_mask_l),
	  .temp_mask (o_mask_h),
	  .o	     (inv)
	);

    A_cse_ocs_dpa_aes10c_gf16_mul_masked
       #( .OCS_AES_DPA_USE_CTECH(OCS_AES_DPA_USE_CTECH),
          .POLY1(POLY1))
      mul_masked1
	(
	  .a	  (a_h),
	  .a_mask (a_mask_h),
	  .b	  (inv),
	  .b_mask (a_mask_l),
	  .o	  (o_h),
	  .o_mask (o_mask_h)
	);
 
    A_cse_ocs_dpa_aes10c_gf16_mul_masked
       #( .OCS_AES_DPA_USE_CTECH(OCS_AES_DPA_USE_CTECH),
          .POLY1(POLY1))
      mul_masked2
	(
	  .a	  (sum_hl),
	  .a_mask (sum_hl_mask),
	  .b	  (inv),
	  .b_mask (a_mask_l),
	  .o	  (o_l),
	  .o_mask (o_mask_l)
	);

endmodule
